﻿using System.Threading.Tasks.Sources;

namespace TimerN
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var loopCount = 10000;
            var delay = TimeSpan.FromMilliseconds(15d);

            const int N = 10000;
            while (loopCount > 0)
            {
                loopCount -= 1;
                _ = LoopAsync(N, delay);
                Console.WriteLine($"loopCount={loopCount}");
            }

            Console.ReadLine();
        }


        async static ValueTask LoopAsync(int n, TimeSpan delay)
        {
            using var taskSource = new DelayTaskSource();
            while (true)
            {
                await taskSource.CreateDelayTask(delay);
            }
        }

        sealed class DelayTaskSource : IValueTaskSource, IDisposable
        {
            private readonly Timer timer;
            private ManualResetValueTaskSourceCore<object?> sourceCore = new();

            public DelayTaskSource()
            {
                this.timer = new Timer(this.TimerCallback, null, Timeout.Infinite, Timeout.Infinite);
            }

            private void TimerCallback(object? state)
            {
                this.sourceCore.SetResult(null);
            }

            public ValueTask CreateDelayTask(TimeSpan delay)
            {
                this.sourceCore.Reset();
                this.timer.Change(delay, Timeout.InfiniteTimeSpan);
                return new ValueTask(this, this.sourceCore.Version);
            }

            public void GetResult(short token)
            {
                sourceCore.GetResult(token);
            }

            public ValueTaskSourceStatus GetStatus(short token)
            {
                return sourceCore.GetStatus(token);
            }

            public void OnCompleted(Action<object?> continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags)
            {
                sourceCore.OnCompleted(continuation, state, token, flags);
            }

            public void Dispose()
            {
                this.timer.Dispose();
            }
        }
    }
}

